﻿#ifndef SIGNAL_SLOT_H
#define SIGNAL_SLOT_H

#include <functional>  // std::function  std::bind
#include <vector>
#include <map>
#include <memory>  // std::shared_ptr    std::unique_ptr  std::weak_ptr
#include <mutex>
//#include <algorithm>

#include <assert.h>

#include "noncopyable.h"

#include "ThreadPool.h"
#include "SignalHandle.h"
#include "Singleton.h"

namespace mufun
{


/**
 * SignalImpl 管理 SlotImpl 数组
 * SlotImpl 记录回调函数
 * Signal 一个信号发出后，执行该信号下的多个回调函数
 */

/**
 * todo
 *
 * 1.多线程实现异步信号调用
 * 2.实现信号管理类，管理多个信号（通过map管理），通过枚举实现对信号的映射，
 * 3.使用单例模式实现信号管理类
 * 4.支持注册时，选择是否异步执行
 * 5.支持发送信号时，如果选择异步，则所有回调异步执行
 * 
 * 6.将该文件的定义移动到cpp文件
 * 7.增加命名空间
 */


template<typename Callback>
struct SlotImpl;

template<typename Callback>
struct SignalImpl : noncopyable
{
    typedef std::vector<std::weak_ptr<SlotImpl<Callback> > > SlotList;

    SignalImpl()
        : slots_(new SlotList)
    {
    }

    void copyOnWrite()
    {
        //mutex_.assertLocked();
        if (!slots_.unique())
        {
            slots_.reset(new SlotList(*slots_));
        }
        assert(slots_.unique());
    }

    void clean()
    {
        std::lock_guard<std::mutex> lock(mutex_);
        copyOnWrite();
        SlotList& list(*slots_);
        typename SlotList::iterator it(list.begin());
        while (it != list.end())
        {
            if (it->expired())
            {
                it = list.erase(it);
            }
            else
            {
                ++it;
            }
        }
    }

    std::mutex mutex_;
    std::shared_ptr<SlotList> slots_;
};

template<typename Callback>
struct SlotImpl : noncopyable
{
    typedef SignalImpl<Callback> Data;
    SlotImpl(const std::shared_ptr<Data>& data, Callback&& cb, bool async)
        : data_(data), cb_(cb), tie_(), tied_(false), index_(0), async_(async)
    {
    }

    SlotImpl(const std::shared_ptr<Data>& data, Callback&& cb,
             const std::shared_ptr<void>& tie, bool async)
        : data_(data), cb_(cb), tie_(tie), tied_(true), index_(0), async_(async)
    {
    }

    ~SlotImpl()
    {
        std::shared_ptr<Data> data(data_.lock());
        if (data)
        {
            data->clean();
        }
    }

    std::weak_ptr<Data> data_;
    Callback cb_;
    std::weak_ptr<void> tie_;
    bool tied_;
    size_t index_;
    bool async_;
};

// Signal class
using Conn = std::shared_ptr<void>;
template<typename T>
class Signal;

template<typename... Args>
class Signal<void(Args...)> : private noncopyable
{
public:
    using Callback = std::function<void(Args...)>;
    using SignalImpl = SignalImpl<Callback>;
    using SlotImpl = SlotImpl<Callback>;

    Signal()
        : impl_(new SignalImpl)
    {
    }

    Signal(std::weak_ptr<ThreadPool> pool)
        : impl_(new SignalImpl), pool_(pool)
    {
    }

    ~Signal()
    {
    }

    Conn connect(Callback func, bool async = false)
    {
        std::shared_ptr<SlotImpl> slotImpl(
            new SlotImpl(impl_, std::move(func), async));
        add(slotImpl);
        return slotImpl;
    }

    /**
     * @brief connect
     * @param func
     * @param tie  tie 成员通过 std::weak_ptr 检查对象的生命周期，避免信号槽机制中调用无效对象的成员函数，假设我们有一个对象 Foo，
     *             它连接了一个信号，但对象可能在信号发射前被销毁。我们需要确保信号触发时不会调用已销毁对象的成员函数。
     * @return
     */
    Conn connect(Callback func, const std::shared_ptr<void>& tie, bool async = false)
    {
        std::shared_ptr<SlotImpl> slotImpl(new SlotImpl(impl_, std::forward<Callback>(func), tie, async));
        add(slotImpl);
        return slotImpl;
    }

    // Disconnect a specific connection
    void disconnect(const std::shared_ptr<void>& slot)
    {
        remove(std::static_pointer_cast<SlotImpl>(slot));
    }

    // Emit the signal
    void call(Args&&... args)
    {
        SignalImpl& impl(*impl_);
        std::shared_ptr<typename SignalImpl::SlotList> slots;
        {
            std::lock_guard<std::mutex> lock(impl.mutex_); // 使用时，拷贝一份信号槽列表，避免多线程同时修改
            slots = impl.slots_;
        }
        typename SignalImpl::SlotList& s(*slots);
        for (typename SignalImpl::SlotList::const_iterator it = s.begin(); it != s.end(); ++it)
        {
            std::shared_ptr<SlotImpl> slotImpl = it->lock();
            if (slotImpl)
            {
                std::weak_ptr<SlotImpl> slot_weak = it->lock();

                auto task = [slot_weak, &args...]
                {
                    std::shared_ptr<SlotImpl> slotImpl = slot_weak.lock();
                    if (slotImpl)
                    {
                        std::shared_ptr<void> guard;
                        if (slotImpl->tied_)
                        {
                            guard = slotImpl->tie_.lock();
                            if (guard)
                            {
                                slotImpl->cb_(args...);
                            }
                        }
                        else
                        {
                            slotImpl->cb_(args...);
                        }
                    }
                    else
                    {
                        // printf("slot is dead\n");
                    }
                };

                std::shared_ptr<ThreadPool> pool = pool_.lock();
                if (slotImpl->async_ && pool)
                {
                    pool->submit(task);
                }
                else
                {
                    task();
                }
            }
        }
    }

    // Alias for call
    void operator()(Args&&... args)
    {
        call(std::forward<Args>(args)...);
    }

private:
    void add(const std::shared_ptr<SlotImpl>& slot)
    {
        SignalImpl& impl(*impl_);
        {
            std::lock_guard<std::mutex> lock(impl.mutex_);
            impl.copyOnWrite();
            // slot->index_ = impl.slots_->size();
            impl.slots_->push_back(slot);
        }
    }

    void remove(const std::shared_ptr<SlotImpl>& slot)
    {
        SignalImpl& impl(*impl_);
        {
            std::lock_guard<std::mutex> lock(impl.mutex_);
            impl.copyOnWrite();

            for (auto it = impl.slots_->begin(); it != impl.slots_->end(); )
            {
                if (it->lock() == slot)
                {
                    it = impl.slots_->erase(it); // 删除元素，并返回下一个有效的迭代器
                }
                else
                {
                    ++it;
                }
            }
        }
    }

    void __call(std::weak_ptr<SlotImpl>& s, Args&&... args)
    {
        std::shared_ptr<SlotImpl> slotImpl = s.lock();
        if (slotImpl)
        {
            std::shared_ptr<void> guard;
            if (slotImpl->tied_)
            {
                guard = slotImpl->tie_.lock();
                if (guard)
                {
                    slotImpl->cb_(args...);
                }
            }
            else
            {
                slotImpl->cb_(args...);
            }
        }
    }

    const std::shared_ptr<SignalImpl> impl_;
    std::weak_ptr<ThreadPool> pool_;
};


//多态与模版结合，实现类型转换
class DownCast : noncopyable
{
public:
    virtual ~DownCast() {};
    virtual std::shared_ptr<void> createSignal(std::map<SignalSign, std::shared_ptr<void>>& sigs, std::shared_ptr<ThreadPool>& pool) = 0;
};

template <typename T, typename... Args>
class DownCastT : public DownCast
{
public:
    using Callback = std::function<void(Args...)>;

    explicit DownCastT(const SignalSign& sign) : sign_(sign)
    {
    }

    virtual std::shared_ptr<void> createSignal(std::map<SignalSign, std::shared_ptr<void>>& sigs, std::shared_ptr<ThreadPool>& pool)
    {
        std::shared_ptr<Signal<T>> pSig(new Signal<T>(pool));
        assert(pSig != nullptr);

        sigs[sign_] = pSig;

        return pSig;
    }

private:
    SignalSign sign_;
};


using SignalList = std::map<SignalSign, std::shared_ptr<void>>;
class SIGNAL : noncopyable
{
public:
    SIGNAL();
    ~SIGNAL();

    void registerDownCast(const SignalSign& sign, const std::shared_ptr<DownCast>& downcast);

public:
    std::shared_ptr<SignalList> signals_;
    std::shared_ptr<ThreadPool> pool_;
    std::mutex mutex_;
    std::map<SignalSign, std::shared_ptr<DownCast>> downcasts_;
};

// class RegisterDownCast
// {
// public:
//     explicit RegisterDownCast( SignalSign sign,  std::shared_ptr<DownCast> downcast)
//     {
//         auto &signal = SingletonStatic<SIGNAL>::instance();
//         signal.registerDownCast(SignalSign::ReadFile, downcast);
//     }
// };

// #define REGISTER_DOWNCAST(sign, FuncType) \
// do\
// {\
//     /*auto downcast = std::make_shared<DownCastT<FuncType>>(sign);*/\
//      RegisterDownCast registerDownCast(sign, std::shared_ptr<DownCastT<FuncType>>(new DownCastT<FuncType>(sign)));\
// } while (0);


//Util::Core::RegisterExtension(__uuidof(ITXContactIMStatusEvtExt), GUID_NULL,NULL, m_pIMContactStatusEvtExt);
#define __RegisterExtension(sign, FuncType, Func, tie, async)                                                       \
    [&](void) -> Conn                                                                                               \
    {                                                                                                               \
        auto &signal = SingletonStatic<SIGNAL>::instance();                                                         \
        Conn pSig;                                                                                                  \
        { /*read operation*/                                                                                        \
            std::shared_ptr<SignalList> signals_tmp;                                                                \
            {                                                                                                       \
                std::lock_guard<std::mutex> lock(signal.mutex_); /*read data, don't need lock when after get data*/ \
                signals_tmp = signal.signals_;                                                                      \
            }                                                                                                       \
                                                                                                                    \
            auto it = signal.signals_->find(sign);                                                                  \
            if (it != signal.signals_->end())                                                                       \
            {                                                                                                       \
                pSig = it->second;                                                                                  \
            }                                                                                                       \
        }                                                                                                           \
                                                                                                                    \
        if (!pSig)            /*if not find, create a new signal*/                                                  \
        { /*write operation*/ /*when write, lock all step, until write finish*/                                     \
            std::lock_guard<std::mutex> lock(signal.mutex_);                                                        \
            if (!signal.signals_.unique())                                                                          \
            {                                                                                                       \
                signal.signals_.reset(new SignalList(*signal.signals_)); /*copyOnWrite*/                            \
            }                                                                                                       \
                                                                                                                    \
            /*REGISTER_DOWNCAST(sign, FuncType);*/                                                                  \
            {                                                                                                       \
                auto downcast = std::make_shared<DownCastT<FuncType>>(sign);                                        \
                signal.registerDownCast(sign, downcast);                                                            \
            }                                                                                                       \
                                                                                                                    \
            auto cast = signal.downcasts_.find(sign);                                                               \
            if (cast != signal.downcasts_.end())                                                                    \
            {                                                                                                       \
                pSig = cast->second->createSignal(*signal.signals_, signal.pool_);                                  \
            }                                                                                                       \
            else                                                                                                    \
            {                                                                                                       \
                const char *str = #sign;                                                                            \
                printf("not find downcast for %s\n", str);                                                          \
                assert(cast != signal.downcasts_.end());                                                            \
            }                                                                                                       \
        }                                                                                                           \
                                                                                                                    \
        if (pSig)                                                                                                   \
        {                                                                                                           \
            std::shared_ptr<Signal<FuncType>> pSig2 = std::static_pointer_cast<Signal<FuncType>>(pSig);             \
                                                                                                                    \
            if (tie)                                                                                                \
            {                                                                                                       \
                auto conn = pSig2->connect(Func, tie, async);                                                       \
                return conn;                                                                                        \
            }                                                                                                       \
            else                                                                                                    \
            {                                                                                                       \
                auto conn = pSig2->connect(Func, async);                                                            \
                return conn;                                                                                        \
            }                                                                                                       \
        }                                                                                                           \
                                                                                                                    \
        return pSig;                                                                                                \
    }()

#define RegisterExtension(sign, Func) __RegisterExtension(SignalSign##::##sign, sign##FuncType, Func, nullptr, false)
#define RegisterExtensionAsync(sign, Func) __RegisterExtension(SignalSign##::##sign, sign##FuncType, Func, nullptr, true)
#define RegisterExtensionTie(sign, Func) __RegisterExtension(SignalSign##::##sign, sign##FuncType, Func, tie, false)
#define RegisterExtensionAsyncTie(sign, Func) __RegisterExtension(SignalSign##::##sign, sign##FuncType, Func, tie, true)


#define UnRegisterExtension(conn) \
    do                            \
    {                             \
        conn = Conn();            \
    } while (0)


//CallExtension(IQiDianStatusMgrExt, OnRefreshTargetGroupStatusOfCCList(&m_mapQQStatus));
#define __CallExtension(sign, FuncType, values)                                                                   \
    do                                                                                                          \
    {                                                                                                           \
        auto &signal = SingletonStatic<SIGNAL>::instance();                                                     \
        std::shared_ptr<SignalList> signals_tmp;                                                                \
        {                                                                                                       \
            std::lock_guard<std::mutex> lock(signal.mutex_); /*read data, don't need lock when after get data*/ \
            signals_tmp = signal.signals_;                                                                      \
        }                                                                                                       \
                                                                                                                \
        auto it = signals_tmp->find(sign);                                                                      \
        if (it != signals_tmp->end())                                                                           \
        {                                                                                                       \
            std::shared_ptr<Signal<FuncType>> sig = std::static_pointer_cast<Signal<FuncType>>(it->second);     \
            sig->call(values);                                                                                  \
        }                                                                                                       \
    } while (0)

#define CallExtension(sign, values) __CallExtension(SignalSign##::##sign, sign##FuncType, values)

} // namespace mufun

#endif // SIGNAL_SLOT_H



//C/C++ 可变参数宏方法总结   https://blog.csdn.net/Sharpneo/article/details/131219346

